/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package station3d.graphics;

import com.sun.opengl.util.texture.Texture;
import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;


import station3d.math.*;

import static javax.media.opengl.GL.*;

/**
 *
 * @author Martin
 */
public class Renderer {


    private int width;
    private int height;

    private GL gl;
    private GLU glu = new GLU();

    private final Vector3D cameraPosition = new Vector3D();
    private float cameraXRotation;
    private float cameraYRotation;

    private Texture texture;
    private Texture texture2;

    private final Vector3D meshPosition = new Vector3D();
    private float meshScale = 1;
    private float meshYRotation = 0;


    /** Inicializuje renderer. */
    public void init() {
        gl.glEnable(GL_NORMALIZE);
        
        gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        gl.glAlphaFunc(GL_GREATER, 0.5f);

        gl.glClearColor(0, 0, 0, 1);
    }

    /** Nastaví novou šířku viewportu. */
    public void setNewWidth(int width) {
        this.width = width;
    }

    /** Nastaví novou výšku viewportu. */
    public void setNewHeight(int height) {
        this.height = height;
    }

    /** Nastaví OpenGL kontext pro daný snímek. */
    public void setGL(GL gl) {
        this.gl = gl;
    }

    /** Začátek snímku. */
    public void startFrame() {
        gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }

    /** Konec snímku. */
    public void endFrame() {
        gl.glFlush();
    }



    /** Nastaví 2D zobrazení. */
    public void setup2D() {
        gl.glMatrixMode(GL_PROJECTION);
        gl.glLoadIdentity();

        gl.glOrtho(0, width, 0, height, -1, -1);
        gl.glScalef(1 / (float)(width / 2), -1 / (float)(height / 2), 1);
        gl.glTranslatef(-(width / 2), -(height / 2), 0);

        gl.glMatrixMode(GL_MODELVIEW);
        gl.glLoadIdentity();

        gl.glDisable(GL_DEPTH_TEST);
        gl.glDisable(GL_LIGHTING);
    }

    /** Nakreslí obdélník. */
    public void fillRect(int x, int y, int width, int height, float r, float g, float b) {
        fillRect(x, y, width, height, r, g, b, 1);
    }

    /** Nakreslí obdélník. */
    public void fillRect(int x, int y, int width, int height, float r, float g, float b, float alpha) {
        gl.glColor4f(r, g, b, alpha);
        gl.glBegin(GL_QUADS);
        gl.glVertex2i(x, y);
        gl.glVertex2i(x, y + height);
        gl.glVertex2i(x + width, y + height);
        gl.glVertex2i(x + width, y);
        gl.glEnd();
    }

    /** Vyplní obrazovku barvou. */
    public void fillScreen(float r, float g, float b, float alpha) {
        fillRect(0, 0, this.width, this.height, r, g, b, alpha);
    }




    /** Vrátí umístění kamery. */
    public Vector3D getCameraPosition() {
        return cameraPosition;
    }

    /** Nastaví novou pozici kamery. */
    public void setCameraPosition(Vector3D position) {
        cameraPosition.setTo(position);
    }

    /** Vrátí rotaci pohledu kamery okolo osy x. */
    public float getCameraXRotation() {
        return cameraXRotation;
    }

    /** Nastaví rotaci pohledu kamery okolo osy x. */
    public void setCameraXRotation(float rotation) {
        cameraXRotation = rotation;
    }

    /** Vrátí rotaci pohledu kamery okolo osy y. */
    public float getCameraYRotation() {
        return cameraXRotation;
    }

    /** Nastaví rotaci pohledu kamery okolo osy y. */
    public void setCameraYRotation(float rotation) {
        cameraYRotation = rotation;
    }



    /** Nastaví 3D zobrazení. */
    public void setup3D() {
        gl.glMatrixMode(GL_PROJECTION);
        gl.glLoadIdentity();

        glu.gluPerspective(45, 1.3333, 1, 1000);

        gl.glMatrixMode(GL_MODELVIEW);
        gl.glLoadIdentity();

        gl.glRotatef(cameraXRotation, 1, 0, 0);
        gl.glRotatef(cameraYRotation - 90, 0, 1, 0);
        gl.glTranslatef(-(float)cameraPosition.x,
                -(float)cameraPosition.y,
                -(float)cameraPosition.z);


        float[] lightpos = {0, 100, 0, 0};
        gl.glLightfv(GL_LIGHT0, GL_POSITION, lightpos, 0);
        float[] ambient = {0.2f, 0.2f, 0.2f, 1};
        gl.glLightfv(GL_LIGHT0, GL_AMBIENT, ambient, 0);
        float[] diffuse = {1, 1, 1, 1};
        gl.glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse, 0);
        float[] specular = {1, 1, 1, 1};
        gl.glLightfv(GL_LIGHT0, GL_SPECULAR, specular, 0);


        /*gl.glEnable(GL_FOG);
        gl.glFogi(GL_FOG_MODE, GL_LINEAR);
        gl.glFogfv(GL_FOG_COLOR, new float[] {178f/255, 205f/255, 226f/255}, 0);
        gl.glFogf(GL_FOG_DENSITY, 0.35f);
        gl.glFogf(GL_FOG_START, 100);
        gl.glFogf(GL_FOG_END, 1000);*/
    }

    public void setTexture(Texture texture) {
        this.texture = texture;
        gl.glActiveTexture(GL_TEXTURE0);
        gl.glBindTexture(GL_TEXTURE_2D, texture.getTextureObject());
    }


    public void enableTexture() {
        gl.glActiveTexture(GL_TEXTURE0);
        gl.glEnable(GL_TEXTURE_2D);
    }

    public void disableTexture() {
        gl.glActiveTexture(GL_TEXTURE0);
        gl.glDisable(GL_TEXTURE_2D);
    }


    public void setTexture2(Texture texture) {
        this.texture = texture;
        gl.glActiveTexture(GL_TEXTURE1);
        gl.glBindTexture(GL_TEXTURE_2D, texture.getTextureObject());
    }


    public void enableTexture2() {
        gl.glActiveTexture(GL_TEXTURE1);
        gl.glEnable(GL_TEXTURE_2D);

        gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
        gl.glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD_SIGNED);
    }

    public void disableTexture2() {
        gl.glActiveTexture(GL_TEXTURE1);
        gl.glDisable(GL_TEXTURE_2D);
    }

    public void enableDepthTest() {
        gl.glEnable(GL_DEPTH_TEST);
    }

    public void disableDepthTest() {
        gl.glDisable(GL_DEPTH_TEST);
    }

    public void enableAlphaTest() {
        gl.glEnable(GL_ALPHA_TEST);
    }

    public void disableAlphaTest() {
        gl.glDisable(GL_ALPHA_TEST);
    }

    public void enableBlending() {
        gl.glEnable(GL_BLEND);
    }

    public void disableBlending() {
        gl.glDisable(GL_BLEND);
    }

    public void enableLight() {
        gl.glEnable(GL_LIGHTING);
        gl.glEnable(GL_LIGHT0);
    }

    public void disableLight() {
        gl.glDisable(GL_LIGHT0);
        gl.glDisable(GL_LIGHTING);
    }

    public void enableFaceCulling() {
        gl.glEnable(GL_CULL_FACE);
    }

    public void disableFaceCulling() {
        gl.glDisable(GL_CULL_FACE);
    }



    public void setMeshPosition(Vector3D meshPosition) {
        this.meshPosition.setTo(meshPosition);
    }

    public void setMeshScale(float meshScale) {
        this.meshScale = meshScale;
    }

    public void setMeshYRotation(float meshYRotation) {
        this.meshYRotation = meshYRotation;
    }

    public void resetMeshTransform() {
        meshPosition.setTo(0, 0, 0);
        meshScale = 1;
        meshYRotation = 0;
    }

    public void drawAxes() {
        gl.glBegin(GL_LINES);
        gl.glColor3f(1, 0, 0); // ÄŤervenĂˇ X
        gl.glVertex3f(0, 0, 0);
        gl.glVertex3f(1, 0, 0);
        gl.glColor3f(0, 1, 0); // zelenĂˇ Y
        gl.glVertex3f(0, 0, 0);
        gl.glVertex3f(0, 1, 0);
        gl.glColor3f(0, 0, 1); // modrĂˇ Z
        gl.glVertex3f(0, 0, 0);
        gl.glVertex3f(0, 0, 1);
        gl.glEnd();

        gl.glColor3f(1, 1, 1);
    }

    public void drawVector(Vector3D position, Vector3D vector) {
        gl.glPushMatrix();
        gl.glTranslatef((float)position.x, (float)position.y, (float)position.z);

        gl.glColor3f(1, 1, 1);
        gl.glBegin(GL_LINES);
        gl.glVertex3f(0, 0, 0);
        gl.glVertex3f((float)vector.x, (float)vector.y, (float)vector.z);
        gl.glEnd();

        gl.glPopMatrix();
    }


    public void drawMesh(Mesh mesh) {

        gl.glPushMatrix();
        gl.glTranslatef((float)meshPosition.x, (float)meshPosition.y, (float)meshPosition.z);
        gl.glScalef(meshScale, meshScale, meshScale);
        gl.glRotatef(meshYRotation, 0, 1, 0);

        float[] mcolor = { 1, 1, 1, 1.0f };
        gl.glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mcolor, 0);

        gl.glEnableClientState(GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL_NORMAL_ARRAY);
        if (mesh.hasTexCoordArray()) gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        gl.glVertexPointer(3, GL_FLOAT, 0, mesh.getVertexArray());
        gl.glNormalPointer(GL_FLOAT, 0, mesh.getNormalArray());
        
        if (mesh.hasTexCoordArray()) {
            gl.glClientActiveTexture(GL_TEXTURE0);
            gl.glTexCoordPointer(2, GL_FLOAT, 0, mesh.getTexCoordArray());
            if (mesh.hasTexCoordArray2()) {
                gl.glClientActiveTexture(GL_TEXTURE1);
                gl.glTexCoordPointer(2, GL_FLOAT, 0, mesh.getTexCoordArray2());
            }
        }

        //System.out.printf("Kreslím\n");
        gl.glDrawArrays(GL_TRIANGLES, 0, mesh.getVerticesCount());

        if (mesh.hasTexCoordArray()) gl.glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        gl.glDisableClientState(GL_NORMAL_ARRAY);
        gl.glDisableClientState(GL_VERTEX_ARRAY);

        gl.glPopMatrix();
    }


    public void drawTerrain(Terrain terrain) {
        setMeshPosition(terrain.getPosition());
        setMeshScale(terrain.getMeshScale());

        setTexture(terrain.getLargeTexture());
        enableTexture();

        setTexture2(terrain.getDetailTexture());
        enableTexture2();

        drawMesh(terrain.getTerrainMesh());

        disableTexture();
        disableTexture2();

        resetMeshTransform();
    }


    public void drawTestPlane(Vector3D position) {
        gl.glPushMatrix();
        gl.glTranslatef((float)position.x, (float)position.y, (float)position.z);

        gl.glBegin(GL_QUADS);
        gl.glColor3f(1, 0, 0);
        gl.glVertex3f(-1, 0, -1);
        gl.glColor3f(0, 1, 0);
        gl.glVertex3f(1, 0, -1);
        gl.glColor3f(0, 0, 1);
        gl.glVertex3f(1, 0, 1);
        gl.glColor3f(1, 1, 0);
        gl.glVertex3f(-1, 0, 1);
        gl.glEnd();

        gl.glPopMatrix();

        gl.glColor3f(1, 1, 1);
    }


}
